package com.kfyang.manualgear.daoplugin.completion;

import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiShortNamesCache;
import com.kfyang.manualgear.daoplugin.util.PsiUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.*;
import org.jetbrains.yaml.psi.impl.YAMLPlainTextImpl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.kfyang.manualgear.daoplugin.util.PsiUtils.findParent;
import static com.kfyang.manualgear.daoplugin.util.PsiUtils.inManualGear;

public class YamlCompletionContributor extends CompletionContributor {
    @Override
    public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
        PsiElement element = parameters.getPosition();
        if (parameters.getCompletionType() != CompletionType.BASIC || !inManualGear(element)) {
            return;
        }

//        while (element != null) {
//            System.out.println(element.getClass());
//            element = element.getParent();
//        }
//        element = parameters.getPosition();

        // 在顶层
        if (inTop(element)) {
            result.addElement(LookupElementBuilder.create(
                    "methodName: \n" +
                        "type: \n" +
                        "sql: ")
                .withPresentableText("method")
                .withTypeText("method")
                .withLookupString("method")
                .withInsertHandler((insertionContext, lookupElement) -> {
                    int insertPosition = insertionContext.getSelectionEndOffset();
                    insertPosition -= 14;
                    insertionContext.getEditor().getCaretModel().getCurrentCaret().moveToOffset(insertPosition);
                })
            );
            result.addElement(LookupElementBuilder.create("methodName: "));
            result.addElement(LookupElementBuilder.create("type: "));
            result.addElement(LookupElementBuilder.create("cols: "));
            result.addElement(LookupElementBuilder.create("sql: "));
            result.addElement(LookupElementBuilder.create("frags: "));
            result.addElement(LookupElementBuilder.create("construct: "));
            result.addElement(LookupElementBuilder.create("resultMap: "));
        } else if (inFrags(element)) {
            result.addElement(LookupElementBuilder.create(
                        "id: \n" +
                    "    sql: \n" +
                    "    cond: "
                )
                .withPresentableText("frag")
                .withLookupString("frag")
                .withInsertHandler((insertionContext, lookupElement) -> {
                    int insertPosition = insertionContext.getSelectionEndOffset();
                    insertPosition -= 21;
                    insertionContext.getEditor().getCaretModel().getCurrentCaret().moveToOffset(insertPosition);
                }));
        } else if (inType(element)) {
            result.addElement(LookupElementBuilder.create("QUERY"));
            result.addElement(LookupElementBuilder.create("MODIFY"));
        } else if(inMethodName(element)) {
            PsiFile topLevelFile = InjectedLanguageManager.getInstance(element.getProject()).getTopLevelFile(element);
            String yamlFileName = topLevelFile.getName();
            String className = yamlFileName.substring(0, yamlFileName.lastIndexOf('.'));

            PsiFile[] classFiles = PsiShortNamesCache.getInstance(element.getProject())
                .getFilesByName(className + ".java");
            PsiJavaFile psiJavaFile = (PsiJavaFile) classFiles[0];

            List<PsiClass> psiClasses = Arrays.stream(psiJavaFile.getClasses())
                .filter(psiClass -> psiClass.getName().equals(className))
                .toList();
            if (psiClasses.size() != 1) {
                return;
            }
            PsiClass psiClass = psiClasses.get(0);
            if (!psiClass.isInterface()) {
                return;
            }
            List<LookupElement> lookupElementList = new ArrayList<>();
            for (PsiMethod interfaceMethod : psiClass.getMethods()) {
                lookupElementList.add(LookupElementBuilder.create(interfaceMethod.getName()));
            }
            result.addAllElements(lookupElementList);
        }

    }

    private static boolean inTop(PsiElement element) {
        return PsiUtils.findParent(element, 3) instanceof YAMLDocument || PsiUtils.findParent(element, 2) instanceof YAMLDocument;
    }

    private static boolean inFrags(PsiElement element) {
        PsiElement tempElement = findParent(element, 2);
        return tempElement instanceof YAMLSequenceItem sequenceItem
            && sequenceItem.getParent() instanceof YAMLSequence yamlSequence
            && yamlSequence.getParent() instanceof YAMLKeyValue keyValue
            && keyValue.getKeyText().equals("frags")
            && keyValue.getParent() instanceof YAMLMapping yamlMapping
            && yamlMapping.getParent() instanceof YAMLDocument;
    }

    private static boolean inType(PsiElement element) {
        return element.getParent() instanceof YAMLPlainTextImpl plainText
            && plainText.getParent() instanceof YAMLKeyValue keyValue
            && keyValue.getKeyText().equals("type")
            && keyValue.getParent() instanceof YAMLMapping yamlMapping
            && yamlMapping.getParent() instanceof YAMLDocument;
    }

    private static boolean inMethodName(PsiElement element) {
        return element.getParent() instanceof YAMLPlainTextImpl plainText
            && plainText.getParent() instanceof YAMLKeyValue keyValue
            && keyValue.getKeyText().equals("methodName")
            && keyValue.getParent() instanceof YAMLMapping yamlMapping
            && yamlMapping.getParent() instanceof YAMLDocument;
    }
}
